/*
----------------------------------------------------------------------------------------------------
This source file is part of the Gamut Framework
Copyright (c) 2007 by Ryan Holcombe

This program is free software; you can redistribute it and/or modify it under the terms of the GNU
Lesser General Public License as published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
----------------------------------------------------------------------------------------------------
*/


#include "GamutStateManager.h"
#include "GamutException.h"
#include "GamutIState.h"
#include "GamutLogger.h"
#include "GamutProfiler.h"

namespace Gamut
{
	// -------------------------------------------------------------------------------------------//
	StateManager::StateManager(const std::string& name, unsigned int priority) :
		IManager(name, priority)
	{
		mStateMap.clear();
		mStack.clear();
	}
	
	// -------------------------------------------------------------------------------------------//
	StateManager::~StateManager()
	{
		// clear the stack
		mStack.clear();		
		
		// destroy all registered states
		while (mStateMap.size() > 0)
		{
			destroyState(mStateMap.begin()->first);
		}
	}
	
	// -------------------------------------------------------------------------------------------//
	void StateManager::registerState(std::string name, IState *state)
	{
		// make sure the state name does not already exist in the map
		if (mStateMap.find(name) != mStateMap.end())
		{
			LOG2("State [" + name + "] already exists in the map");
			return;
		}
		
		// make sure the pointer does not already exists
		for (StateMap::iterator iter = mStateMap.begin(); iter != mStateMap.end(); iter++)
		{
			if (state == iter->second)
				throw Exception("StateManager::registerState", "State pointer to [" + name +
								"] already exists in the map as [" + iter->first + "]");
		}

		state->onStateAdd();
		
		mStateMap.insert(name,state);
		LOGF3("State [%s] (%p) registered", name.c_str(), state);
	}
		
	// -------------------------------------------------------------------------------------------//
	void StateManager::destroyState(std::string name)
	{
		StateMap::iterator iter = mStateMap.find(name);
		
		// make sure the name exists in the map
		if (iter == mStateMap.end())
		{
			LOG2("Unable to find state [" + name + "] in map");
			return;
		}
		
		iter->second->onStateDestroy();
		mStateMap.erase(iter);
		LOG3("State [" + name + "] destroyed");
	}
	
	// -------------------------------------------------------------------------------------------//
	void StateManager::push(std::string name)
	{
		// make sure it does not already exist in the list
		if (std::find(mPush.begin(), mPush.end(), name) != mPush.end())
		{
			LOG4("State [" + name + "] is already flagged to be pushed");
			return;
		}
		
		mPush.push_back(name);
	}

	// -------------------------------------------------------------------------------------------//
	void StateManager::pop(std::string name)
	{		
		// make sure it does not already exist in the list
		if (std::find(mPop.begin(), mPop.end(), name) != mPop.end())
		{
			LOG4("State [" + name + "] is already flagged to be popped");
			return;
		}
		
		mPop.push_back(name);
	}

	// -------------------------------------------------------------------------------------------//
	void StateManager::tick(double elapsed)
	{
		std::string name;
		
		// pop all flagged states
		for (int i=0; i<mPop.size(); i++)
			_popState(mPop[i]);
		mPop.clear();
		
		// push all flagged states
		for (int i=0; i<mPush.size(); i++)
			_pushState(mPush[i]);
		mPush.clear();

		
		// go through each item in the stack, running the top member in tick mode
		StateList::iterator iter = mStack.begin();
		while (iter != mStack.end())
		{
			// if unable to find the name of the state, pop it from the stack
			name = _getName((*iter));
			if (name.size() == 0)
			{
				LOGF2("Unable to find the name of a state (%p) on the stack, popping it", (*iter));
				_popState((*iter));
				return;
			}
			
			PROFILE_START(name);
			if (iter == mStack.begin())
			{
				if ((*iter)->tickState(elapsed) == 0)
					mPop.push_back(name);
			}
			else
			{
				if ((*iter)->pauseState(elapsed) == 0)
					mPop.push_back(name);
			}
			PROFILE_STOP(name);
			
			iter++;
		}
	}
	
	// -------------------------------------------------------------------------------------------//
	bool StateManager::onStack(std::string name)
	{
		// make sure the state exists in the map
		StateMap::iterator iter = mStateMap.find(name);
		if (iter == mStateMap.end())
			return (0);
		
		// search for the state on the stack
		if (std::find(mStack.begin(), mStack.end(), iter->second) == mStack.end())
			return (0);
		
		return (1);		
	}

	// -------------------------------------------------------------------------------------------//
	std::string StateManager::_getName(IState *state)
	{
		StateMap::iterator iter = mStateMap.begin();
		while (iter != mStateMap.end())
		{
			// If we cannot find the state name, there is something wrong
			if (iter->second == state)
			{
				return (iter->first);
			}
			iter++;
		}
		
		return ("");
	}
	
	
	
	
	
	
	
	
	// -------------------------------------------------------------------------------------------//
	void StateManager::_pushState(std::string name)
	{
		StateMap::iterator iter = mStateMap.find(name);
		// make sure the name exists in the map
		if (iter == mStateMap.end())
			throw Exception("StateManager::_pushState", "Unable to find state [" + name + "] in map");
		
		// make sure the item is not already on the stack, push it to the top if it is
		StateList::iterator iter2 = mStack.begin();
		while (iter2 != mStack.end())
		{
			if ((*iter2) == iter->second)
			{
				mStack.erase(iter2);
				mStack.push_front(iter->second);
				LOG3("State [" + iter->first + "] moved to the top of the stack");
				return;
			}
			iter2++;
		}

		iter->second->onStatePush();
		
		mStack.push_front(iter->second);
		LOG3("State [" + iter->first + "] pushed onto the stack");
	}
	
	// -------------------------------------------------------------------------------------------//
	void StateManager::_popState(std::string name)
	{
		// check to see if the name of the state is in the map
		StateMap::iterator sIter = mStateMap.find(name);
		if (sIter == mStateMap.end())
		{
			LOG2("Unable to find state [" + name + "] in map");
			return;
		}
		
		// make sure the stack is not empty
		if (stackSize() == 0)
		{
			LOG2("Stack is empty, unable to pop [" + name + "]");
			return;
		}
		
		// find the state and pop it
		for (StateList::iterator iter = mStack.begin(); iter != mStack.end(); iter++)
		{
			if ((*iter) == sIter->second)
			{
				(*iter)->onStatePop();
				mStack.erase(iter);
				LOG3("State [" + sIter->first + "] popped from the stack");
				return;
			}
		}
		
		// if we get to here then we were unable to find the state on the stack
		LOG2("Unable to find the state [" + name + "] on the stack");
	}
	
	// -------------------------------------------------------------------------------------------//
	void StateManager::_popState(IState *state)
	{
		std::string name;
		
		StateList::iterator iter = mStack.begin();
		
		name = _getName(state);
		if (name.size() == 0)
		{
			LOGF2("Unable to find the state (%p) in the stack", (*iter));
			return;
		}
		
		_popState(name);
	}	
}
